<?php

class MCrypt {

  /** @var string The string to work on. */
  private $string = "";

  /** @var string The key to (de)crypt the string. */
  private $key = "";

  /** @var integer The size of the key to crypt the string. */
  private $key_size = -1;

  /** @var resource The resource used to init the mcrypt module. */
  private $resource = NULL;

  /**
   * Initialize the mcrypt module with the given key.
   * It also store the string in private for further use of (de)crypt().
   *
   * @param String $string The string to (de)crypt.
   * @param String $key The key to use to (de)crypt.
   */
  public function __construct(IString $string, IString $key) {
    $this->string = $string->toString();
    $this->openModule();
    $this->createVector();
    $this->getMaximumKeySize();
    $this->setKey($key);
    $this->init();
  }

  /** Deinit and close the resource of the mcrypt module. */
  public function __destruct() {
    $this->deInit();
    $this->close();
  }

  /**
   * Returns the string after (de)crypting.
   * @return String The (de)crypted string.
   */
  public function getString() {
    return new String($this->string);
  }

  /**
   * Opens the module of the algorithm and the mode to be used.
   * @link http://fr2.php.net/manual/en/function.mcrypt-module-open.php
   */
  private function openModule() {
    $this->resource = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_ECB, '');
  }

  /**
   * Create an initialization vector (IV) from a random source.
   * @link http://fr2.php.net/manual/en/function.mcrypt-create-iv.php
   */
  private function createVector() {
    $this->vector = mcrypt_create_iv(mcrypt_enc_get_iv_size($this->resource), MCRYPT_RAND);
  }

  /**
   * Returns the maximum supported keysize of the opened mode.
   * @link http://fr2.php.net/manual/en/function.mcrypt-enc-get-key-size.php
   */
  private function getMaximumKeySize() {
    $this->key_size = mcrypt_enc_get_key_size($this->resource);
  }

  /**
   * Set the hash key to (de)crypt the string.
   * @param String $key The key to use to (de)crypt the string.
   */
  private function setKey(IString $key) {
    $this->key = substr(md5($key), 0, $this->key_size);
  }

  /**
   * This function initializes all buffers needed for encryption.
   * @link http://fr2.php.net/manual/en/function.mcrypt-generic-init.php
   */
  private function init() {
    mcrypt_generic_init($this->resource, $this->key, $this->vector);
  }

  /**
   * This function deinitializes an encryption module.
   * @link http://fr2.php.net/manual/en/function.mcrypt-generic-deinit.php
   */
  private function deInit() {
    mcrypt_generic_deinit($this->resource);
  }

  /**
   * Close the mcrypt module.
   * @link http://fr2.php.net/manual/en/function.mcrypt-module-close.php
   */
  private function close() {
    mcrypt_module_close($this->resource);
  }

  /**
   * This function encrypts data.
   * @return String The crypted string.
   * @link http://fr2.php.net/manual/en/function.mcrypt-generic.php
   * @link http://fr2.php.net/manual/en/function.urldecode.php
   * @link http://fr2.php.net/manual/en/function.urlencode.php
   * @link http://fr2.php.net/manual/en/function.base64-encode.php
   */
  public function crypt() {
    $this->string = mcrypt_generic($this->resource, $this->string);

    return new String($this->string);
  }

  /**
   * Decrypt data.
   * @return String The decrypted string.
   * @link http://fr2.php.net/manual/en/function.mdecrypt-generic.php
   * @link http://fr2.php.net/manual/en/function.urldecode.php
   * @link http://fr2.php.net/manual/en/function.base64-encode.php
   */
  public function decrypt() {
    $this->string = mdecrypt_generic($this->resource, $this->string);

    return new String($this->string);
  }

}

?>
